
#ifndef TOKZRGENTR_DATAS_OUTPUT_TEMPLATES_H
#define TOKZRGENTR_DATAS_OUTPUT_TEMPLATES_H

//#	Define some templates type for generate the output file

//#	This class is responsable for constructing the enum Value
//#	of the target generated .h/.cpp file
#include"internal_tokenizer.h"
#include"u16string_ref.h"
#include"utils.h"
#include<vector>
#include<memory>
namespace TokzrGentr
{
	class internal_tokenizer;

	class BaseOutputTemplate
	{
	public:
		using String = std::u16string;
		BaseOutputTemplate(){};
		virtual ~BaseOutputTemplate() {};
		virtual std::u16string Dump(internal_tokenizer* tokenizer) = 0;
	protected:
	};

	class CppOutputTemplate : public BaseOutputTemplate
	{
	public:
		CppOutputTemplate()
		{
			
		}
		~CppOutputTemplate() {}
		std::u16string Dump(internal_tokenizer* tokenizer)
		{
			auto num = max(
				tokenizer->_connected_table->status_num(),
				tokenizer->_connected_table->value_num()
			);
			_num2str_ht.reserve(static_cast<size_t>(num) + 1);

			//# initialize the hash table for num for string
			//#	TODO: need to opt.
			for (size_t i = 0; i < _num2str_ht.capacity(); i++)
				_num2str_ht.push_back(utf8_to_utf16(to_string(i)));
			
			return
				u"#ifndef TOKZRGENTR_OUTPUT_TOKENIZERS_" + TokzrClassName + u"_H\n"
				u"#define TOKZRGENTR_OUTPUT_TOKENIZERS_" + TokzrClassName + u"_H\n"
				u"#include <string>\n"
				u"namespace " + NameSpaceName + u"\n{\n"
				+ DumpEnumTypes(tokenizer->_token_types)
				+ u"class " + TokzrClassName + u"\n{\npublic:\n"
				+ DumpTokenStruct()
				+ DumpAlgoritms()
				+ DumpInternalDatasAPI()
				+ u"private:\n"
				+ DumpInternalDatas()
				+ DumpStaticDatas(tokenizer)
				+ u"};\n"
				+ DumpStaticCharSetTable(tokenizer->_charset_table)
				+ DumpStaticConnectedTable(tokenizer->_connected_table)
				+ DumpStaticEnumTypesArray(tokenizer->_token_types)
				+ u"};\n"
				+ u"#endif//" + TokzrClassName;
		}
		//#	helper func 
		String TokenClassName = u"Token";
		String TokenTagClassName = u"TokenTag";
		String EndOfTokenTagStr = u"END_OF_TOKEN";
		String TokzrClassName = u"Tokenizer";
		String NameSpaceName = u"TokzrGentr";
	private:
		inline std::u16string DumpEnumTypes(const std::vector<std::u16string> &TokenTypesList)
		{
			String definitions;
			definitions.append(u"enum " + TokenTagClassName + u" {\n");
			for (size_t i = 1;i<TokenTypesList.size();i++)
			{
				definitions.append(TokenTypesList[i]);
				definitions.append(u",\n");
			}
			definitions.append(u"END_OF_TOKEN\n");
			definitions.append(u"};\n");
			return definitions;
		}
		inline std::u16string DumpStaticEnumTypesArray(const std::vector<std::u16string> &TokenTypesList)
		{
			String typesArraysContnets;
			typesArraysContnets.append(u"END_OF_TOKEN,");
			for (size_t i = 1; i<TokenTypesList.size(); i++)
			{
				typesArraysContnets.append(TokenTypesList[i]);
				typesArraysContnets.push_back(u',');
			}
			typesArraysContnets.pop_back();

			String typesArray = u"const " + TokenTagClassName + u" " + TokzrClassName +  u"::mTypesArray[" +utf8_to_utf16(to_string(TokenTypesList.size())) + u"] = {\n" + typesArraysContnets + u"\n};\n";
			return typesArray;
		}
		inline std::u16string DumpStaticCharSetTable(std::shared_ptr<char_sets_table> CharSetsTable)
		{
			String num_type;
			if (CharSetsTable->value_num() <= 255)
				num_type = u"const unsigned char ";
			else
				num_type = u"const unsigned short ";
			
			String contents;
			size_t cnt = 0;
			for (auto val : *CharSetsTable)
			{
				if (cnt == 100)
				{
					contents.append(u"\n");
					cnt = 0;
				}
				contents.append(_num2str_ht[val]);
				contents.push_back(u',');
				cnt++;
			}
			contents.pop_back();
			String declares = num_type + TokzrClassName + u"::mCharSetsTable[" + utf8_to_utf16(to_string(CharSetsTable->size())) + u"] = {\n" + contents + u"\n};\n";
			return declares;
		}
		inline std::u16string DumpStaticConnectedTable(std::shared_ptr<connected_table> ConnectedTable)
		{
			String contents;
			for (unsigned int row = 0; row < ConnectedTable->status_num(); row++)
			{
				for (unsigned int col = 0; col < ConnectedTable->value_num(); col++)
				{
					contents.append(_num2str_ht[(*ConnectedTable)[row][col]]);
					contents.push_back(u',');
				}
				contents.push_back(u'\n');
			}
			contents.pop_back();
			contents.pop_back();
			contents.push_back(u'\n');

			String numType;
			if (ConnectedTable->status_num() <= 255)
				numType = u"const unsigned char ";
			else if (ConnectedTable->status_num() <= 65535)
				numType = u"const unsigned short ";
			else
				numType = u"unsigned ";

			String startStatusDefinitions = numType + TokzrClassName +u"::mStartStatus = " + utf8_to_utf16(to_string(ConnectedTable->start_status())) + u";\n";
			String invalidStatusDefinitions = numType + TokzrClassName + u"::mInvalidStatus = " + utf8_to_utf16(to_string(ConnectedTable->status_num())) + u";\n";
			String statusNumDefinitions = numType + TokzrClassName + u"::mStatusNum = " + utf8_to_utf16(to_string(ConnectedTable->status_num())) + u";\n";
			String valueNumDefinitions = u"const size_t " + TokzrClassName + u"::mValueNum = " + utf8_to_utf16(to_string(ConnectedTable->value_num())) + u";\n";

			String declares = numType + TokzrClassName + u"::mConnectedTable[" + utf8_to_utf16(to_string(ConnectedTable->status_num())) + u"][" 
				+ utf8_to_utf16(to_string(ConnectedTable->value_num()))
				+  u"] = {\n" + contents + u"};\n";

			contents.clear();
			for (unsigned int i = 0; i < ConnectedTable->status_num(); i++)
			{
				contents.append(_num2str_ht[ConnectedTable->get_mark(i)]);
				contents.push_back(u',');
			}
			contents.pop_back();

			String marks = numType + TokzrClassName + u"::mStatusMarks[" + utf8_to_utf16(to_string(ConnectedTable->status_num())) +u"] = {\n" + contents + u"\n};\n";

			String definitions = startStatusDefinitions + statusNumDefinitions + valueNumDefinitions + invalidStatusDefinitions + declares + marks;
			return definitions;
		}
		inline std::u16string DumpTokenStruct()
		{
			String definitions = u"struct "+ TokenClassName + u"\n{\n"
				u"\t"+TokenTagClassName + u" _id;\n"
				u"\tstd::u16string _contents;\n"
				u"};\n";
			return definitions;
		}
		//TODO:
		inline std::u16string DumpStaticDatas(internal_tokenizer *tokzr) 
		{ 
			String connectdTableType,charSetTableType,statusMarksType;
			if (tokzr->_connected_table->status_num() <= 255)
				connectdTableType = statusMarksType = u"const static unsigned char ";
			else if (tokzr->_connected_table->status_num() <= 65535)
				connectdTableType = statusMarksType = u"const static unsigned short ";
			else
				connectdTableType = charSetTableType = u"unsigned int ";

			if (tokzr->_charset_table->value_num() <= 255)
				charSetTableType = u"const static unsigned char ";
			else
				charSetTableType = u"const static unsigned short ";

			String statusNumStr = utf8_to_utf16(to_string(tokzr->_connected_table->status_num()));
			String valueNumStr = utf8_to_utf16(to_string(tokzr->_connected_table->value_num()));
			String &marksNumStr = _num2str_ht[tokzr->_token_types.size()];
			String declares =
				charSetTableType + u" mCharSetsTable[" + utf8_to_utf16(to_string(tokzr->_charset_table->size())) + u"];\n"
				+ statusMarksType + u" mStatusMarks[" + statusNumStr + u"];\n"
				+ connectdTableType + u" mConnectedTable[" + statusNumStr + u"][" + valueNumStr + u"];\n"
				+ u"const static " + TokenTagClassName + u" mTypesArray[" + marksNumStr + u"];\n"
				+ u"const static size_t mValueNum;\n"
				+ connectdTableType + u" mInvalidStatus;\n"
				+ connectdTableType + u" mStatusNum;\n"
				+ connectdTableType + u" mStartStatus;\n";

			return declares;
		}
		inline std::u16string DumpInternalDatas()
		{
			String strToTokenizeDefinitions = u"std::u16string mStr = u\"\";\n";
			String currentIndexDefinitions = u"size_t mCurrentIndex = 0;\n";
			String recognizedToksNumDefinitions = u"size_t mRecognizedToksNum = 0;\n";
			String isFailDefinitions = u"bool mIsFail = false;\n";
			String ignoreFailDefinitions = u"bool mIsIgnoreFail = true;\n";
			return strToTokenizeDefinitions + currentIndexDefinitions + recognizedToksNumDefinitions + isFailDefinitions + ignoreFailDefinitions;
		}
		inline std::u16string DumpInternalDatasAPI()
		{
			String setStr = u"void SetStr(std::u16string str)\n{\n"
				u"\tmStr = str;\n"
				u"\tmIsFail = false;\n"
				u"\tmCurrentIndex = 0;\n"
				u"\tmRecognizedToksNum = 0;\n"
				u"}\n";
			String setIgnoredFail = u"void SetIgnoreFail(bool mode)\n{\n"
				u"\tmIsIgnoreFail = mode;\n}\n";
			String isIgnoreFail = u"bool IsIgnoreFail() const \n{\n"
				u"\treturn mIsIgnoreFail;\n}\n";
			String isFail = u"bool IsFail() const \n{\n"
				u"\treturn (!mIsIgnoreFail&&mIsFail);\n}\n";
			String isEOF = u"bool IsEOF() const \n{\n"
				u"\treturn mCurrentIndex >= mStr.length();\n}\n";
			String recognizeTokNum = u"size_t RecognizedTokNum() const \n{\n"
				u"\treturn mRecognizedToksNum;\n}\n";
			return setStr + setIgnoredFail + isFail + isIgnoreFail + isEOF + recognizeTokNum;
		}
		inline std::u16string DumpAlgoritms()
		{
			String algoritms = TokenClassName + u" NextTok()\n{\n"
				u"\tstd::remove_const<decltype(mCurrentIndex)>::type chIndex = mCurrentIndex;\n"
				u"\tstd::remove_const<decltype(mStartStatus)>::type currentStatus = mStartStatus;\n"
				u"\tdecltype(currentStatus) lastAcceptedStatus = currentStatus;\n"
				u"\tdecltype(mCurrentIndex) lastAcceptedIndex = chIndex;\n"

				u"\twhile(currentStatus != mInvalidStatus)\n{\n"
				u"\t\tauto ch = mStr[chIndex];\n"
				u"\t\tcurrentStatus = mConnectedTable[currentStatus][mCharSetsTable[ch]];\n"
				u"\t\tchIndex++;\n"
				u"\t\tif(currentStatus != mInvalidStatus && mStatusMarks[currentStatus] != 0)\n"
				u"\t\t{\n"
				u"\t\t\tlastAcceptedStatus = currentStatus;\n"
				u"\t\t\tlastAcceptedIndex = chIndex;\n"
				u"\t\t}\n"
				u"\t}\n"

				u"\tif(lastAcceptedIndex != mCurrentIndex)\n"
				u"\t{\n"
				u"\t\tauto id = mTypesArray[mStatusMarks[lastAcceptedStatus]];\n"
				u"\t\tauto content = mStr.substr(mCurrentIndex,lastAcceptedIndex-mCurrentIndex);\n"
				u"\t\t" + TokenClassName + u" token = {id,content};\n"
				u"\t\tmCurrentIndex = lastAcceptedIndex;\n"
				u"\t\tmRecognizedToksNum++;\n"
				u"\t\treturn token;\n"
				u"\t}\n"

				u"\telse\n\t{\n"
				u"\t\tmIsFail = true;\n"
				u"\t\tif(mIsIgnoreFail&&!IsEOF())\n"
				u"\t\t{\n"
				u"\t\t\tmCurrentIndex += 1;\n"
				u"\t\t\treturn NextTok();\n"
				u"\t\t}\n"
				u"\t\t return {" + EndOfTokenTagStr + u",""};\n"
				u"\t}\n"
				u"}\n";
			return algoritms;
		}
		//#	Vars
		std::vector<String> _num2str_ht;
	};
}
#endif // !TOKZRGENTR_DATAS_OUTPUT_TEMPLATES_H
